#include <jni.h>
#include <string>
#include "include/Link.h"
#include "include/LinkTrack.h"
#include "include/LinkLogger.h"
#include "include/LinkContext.h"

//全局变量
static jobject writeObject = nullptr;
static jobject readObject = nullptr;
static jobject eventObject = nullptr;
static jobject queryObject = nullptr;
//static jobject eventObject = nullptr;
//static jobject eventObject = nullptr;

static JavaVM *g_vm = nullptr;
static jobject g_object = nullptr;

//获取jniEnv
JNIEnv *getJniEnv() {
    JNIEnv *env = nullptr;
    if (g_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
        if (g_vm->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&env), nullptr) != JNI_OK) {
            LINK_LOG_DEBUG("fail to attach current thread");
            return nullptr;
        }
    }

    return env;
}

void releaseJniEnv() {
    if (g_vm->GetEnv(nullptr, JNI_VERSION_1_6) == JNI_EDETACHED) {
        g_vm->DetachCurrentThread();
    }
}

void WriteFn(int cmdCode, void *buffer, int length, bool isResponse) {

    LINK_LOG_DEBUG("write fn in c++");
    JNIEnv *env = getJniEnv();
    if (!env) {
        LINK_LOG_DEBUG("fail to get jnienv in writeFn");
        releaseJniEnv();
        return;
    }

    jclass callClass = env->GetObjectClass(writeObject);
    if (!callClass) {
        LINK_LOG_DEBUG("fail to get writeClass in writeFN");
        releaseJniEnv();
        return;
    }

    jmethodID callMethod = env->GetMethodID(callClass, "writeFn", "([BIZ)V");
    if (!callMethod) {
        LINK_LOG_DEBUG("fail to get writeMethod in writeFN");
        releaseJniEnv();
        return;
    }

    // 将 buffer 转换为 char*
    unsigned char *charBuffer = static_cast<unsigned char *>(buffer);

    // 打印 buffer 内容
    for (int i = 0; i < length; ++i) {
        LINK_LOG_DEBUG("Buffer content:Byte %d: 0x%02x ", i, charBuffer[i]);

    }
    jbyteArray jcharArray1 = env->NewByteArray(length);
    if (!jcharArray1) {
        LINK_LOG_DEBUG("fail to get char array in writeFN");
        releaseJniEnv();
        return;
    }


    env->SetByteArrayRegion(jcharArray1, 0, length, static_cast<const jbyte *>(buffer));
    env->CallVoidMethod(writeObject, callMethod, jcharArray1, length, isResponse);


    env->DeleteLocalRef(callClass);
    env->DeleteLocalRef(jcharArray1);

    releaseJniEnv();
}

void ReadFn(LinkContextRef context, int cmdCode, int errCode) {

    JNIEnv *env = getJniEnv();
    if (!env) {
        LINK_LOG_DEBUG("fail to get jniEnv in readFn");
        releaseJniEnv();
        return;
    }

    jclass callClass = env->GetObjectClass(readObject);
    if (!callClass) {
        LINK_LOG_DEBUG("fail to get readClass in readFn");
        releaseJniEnv();
        return;
    }

    jmethodID callMethod = env->GetMethodID(callClass, "notifyReadFn", "()V");
    if (!callMethod) {
        LINK_LOG_DEBUG("fail to get readMethod in readFn");
        releaseJniEnv();
        return;
    }

    env->CallVoidMethod(readObject, callMethod);
    env->DeleteLocalRef(callClass);

    releaseJniEnv();
}

void EventFn(LinkContextRef context, int eventId) {
    LINK_LOG_DEBUG("event  fn in c++");
    JNIEnv *env = getJniEnv();
    if (!env) {
        LINK_LOG_DEBUG("fail to get jnienv in writeFn");
        releaseJniEnv();
        return;
    }

    jclass callClass = env->GetObjectClass(eventObject);
    if (!callClass) {
        LINK_LOG_DEBUG("fail to get writeClass in writeFN");
        releaseJniEnv();
        return;
    }

    jmethodID callMethod = env->GetMethodID(callClass, "handleEvent", "(I)V");
    if (!callMethod) {
        LINK_LOG_DEBUG("fail to get writeMethod in writeFN");
        releaseJniEnv();
        return;
    }

    env->CallVoidMethod(eventObject, callMethod, eventId);
    env->DeleteLocalRef(callClass);

    releaseJniEnv();
}

void QueryAttributeFn(LinkContextRef context, bool result, bool isBusy) {
    LINK_LOG_DEBUG("query  fn in c++");
    JNIEnv *env = getJniEnv();
    if (!env) {
        LINK_LOG_DEBUG("fail to get jnienv in writeFn");
        releaseJniEnv();
        return;
    }

    jclass callClass = env->GetObjectClass(queryObject);
    if (!callClass) {
        LINK_LOG_DEBUG("fail to get queryClass in queryFN");
        releaseJniEnv();
        return;
    }

    jmethodID callMethod = env->GetMethodID(callClass, "queryStatus", "(ZZ)V");
    if (!callMethod) {
        LINK_LOG_DEBUG("fail to get queryMethod in queryFN");
        releaseJniEnv();
        return;
    }

    env->CallVoidMethod(queryObject, callMethod, result, isBusy);
    env->DeleteLocalRef(callClass);

    releaseJniEnv();
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_linktestapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";

//    jniEnv = *env;
    HHLink::init();
    HHLink::setWriteDataFn(WriteFn);


    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_initResource(JNIEnv *env, jobject thiz,
                                                               jobject write_fn_callback,
                                                               jobject read_fn_callback,
                                                               jobject event_fn_callback) {
    // TODO: implement initResource()


    (*env).GetJavaVM(&g_vm);
    g_object = (*env).NewGlobalRef(thiz);
    readObject = (*env).NewGlobalRef(read_fn_callback);
    writeObject = (*env).NewGlobalRef(write_fn_callback);
    eventObject = (*env).NewGlobalRef(event_fn_callback);


    HHLink::init();
    HHLink::setWriteDataFn(WriteFn);
    HHLink::setReadDataResultFn(ReadFn);
    HHLink::setEventFn(EventFn);


}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_set(JNIEnv *env, jobject thiz) {
    // TODO: implement set()

}


extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_release(JNIEnv *env, jobject thiz) {
    // TODO: implement release()
    HHLink::clear();
    HHLink::release();

    env->DeleteGlobalRef(readObject);
    env->DeleteGlobalRef(writeObject);
    env->DeleteGlobalRef(eventObject);
    env->DeleteGlobalRef(queryObject);

}


extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_connect(JNIEnv *env, jobject thiz, jstring cname,
                                                          jstring cmac,
                                                          jobject query_fn_callback) {
    // TODO: implement connect()
    HHLink::connect(env->GetStringUTFChars(cname, nullptr), env->GetStringUTFChars(cmac, nullptr));

    char buffer[] = {0x01, 0x02, '\0'};
    WriteFn(0, buffer, 2, JNI_TRUE);


}


extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_traceStart(JNIEnv *env, jobject thiz,
                                                             jboolean is_camera_front,
                                                             jobject orientation) {
    // TODO: implement traceStart()
    HHLinkOrientation hhLinkOrientation;
    hhLinkOrientation = HHLinkOrientation::LANDSCAPE_LEFT;

    HHLink::TrackStart(is_camera_front, hhLinkOrientation);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_traceUpdate(JNIEnv *env, jobject thiz,
                                                              jfloat center_x, jfloat center_y,
                                                              jfloat w, jfloat h) {
    // TODO: implement traceUpdate()
    HHLink::TrackUpdate(center_x, center_y, w, h);

}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_traceEnd(JNIEnv *env, jobject thiz) {
    // TODO: implement traceEnd()

}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_closeMiddle(JNIEnv *env, jclass clazz) {
    // TODO: implement closeMiddle()
    HHLink::writeBuffer(HHLink::cmd_centering());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_readData(JNIEnv *env, jclass clazz,
                                                           jbyteArray data, jint length) {
    // TODO: implement readData()

    jbyte *byteArray = env->GetByteArrayElements(data, NULL);


    HHLink::readData(byteArray, length);

    env->ReleaseByteArrayElements(data, byteArray, JNI_COMMIT);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_speedCtrlTrack(JNIEnv *env, jclass clazz,
                                                                 jint pan, jint tilt) {
    // TODO: implement speedCtrlTrack()
    HHLink::writeBuffer(HHLink::cmd_speed_ctrl_track(pan, tilt));
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_setCtrlTrackOri(JNIEnv *env, jclass clazz,
                                                                  jint reverse) {
    // TODO: implement setCtrlTrackOri()
    HHLink::writeBuffer(HHLink::cmd_set_rocker_pan_speed(reverse));
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_linktestapplication_MainActivity_getCtrlTrackOri(JNIEnv *env, jclass clazz) {
    // TODO: implement getCtrlTrackOri()
    return HHLink::ContextGetRockerTiltDirection(HHLink::GetContext());
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_linktestapplication_MainActivity_setRemoteRockerControl(JNIEnv *env, jclass clazz,
                                                                         jfloat x, jfloat y) {
    // TODO: implement setRemoteRockerControl()
    HHLink::setRemoteRockerControl(x, y);
}